package com.powernode.ssm.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import com.lowagie.text.Document;
import com.powernode.ssm.dao.LogInfoDao;
import com.powernode.ssm.dao.UserDao;
import com.powernode.ssm.exception.BusinessException;
import com.powernode.ssm.pojo.bo.XmlToObjectAllInOne;
import com.powernode.ssm.pojo.po.H5UserinfoPo;
import com.powernode.ssm.pojo.po.UserLoginToken;
import com.powernode.ssm.pojo.vo.SaveLogInfoVo;
import com.powernode.ssm.service.RedisService;
import com.powernode.ssm.service.RedisServiceWithFallback;
import com.powernode.ssm.service.UserLoginTokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * Spring 工具类，用于处理用户登录态、日志保存、Token 校验等通用逻辑
 */
@Component
@Slf4j
public class SpringUtils {

    @Autowired
    LogInfoDao logInfoDao;

    @Autowired
    SpringUtils springUtils;

    @Autowired
    UserDao userDao;

    public long baseExpireTime = 300; // 设置最低过期时间,单位为s

    @Autowired
    private RedisServiceWithFallback redisService;

    @Autowired
    private UserLoginTokenService userLoginTokenService; // 用于降级时查询数据库 token

    @Value("${myapp.code}")
    private String appCode;

    // 你可以自定义 Token 的 Header 名称，比如 "Authorization" 或者 "X-Token"
    private static final String TOKEN_HEADER = "Authorization";

    /**
     * 从请求中获取用户信息（根据 Token 解密得到 userid，再查库）
     */
    public H5UserinfoPo getUserByRequest(HttpServletRequest request) {
        String token = (String) request.getAttribute("token");
        String userid = TokenUtils.decrypt(token);
        return userDao.queryUserById(userid, appCode);
    }

    /**
     * 根据 token 字符串获取用户信息
     */
    public H5UserinfoPo getUserByToken(String token) {
        String userid = TokenUtils.decrypt(token);
        return userDao.queryUserById(userid, appCode);
    }

    /**
     * 从请求头中获取 Token，进行基础校验，如果无效返回 null 并响应 401
     */
    public String getToken(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 从请求头中获取 Token
        String token = request.getHeader(TOKEN_HEADER);
        response.setCharacterEncoding("utf-8");
        ResponseUtils responseUtils = new ResponseUtils();

        if (token == null || token.trim().isEmpty()) {
            // 如果没有 Token，返回 401 未授权
            responseUtils.setCode(ReturnCodeEnum.MISS_TOKEN_ERR.getCode());
            responseUtils.setMsg(ReturnCodeEnum.MISS_TOKEN_ERR.getMsg());
            responseUtils.setData(null);
            response.getWriter().write(String.valueOf(new JSONObject(responseUtils)));
            return null;
        }

        // 这里可以添加更复杂的 Token 校验逻辑，比如解析 JWT，校验签名、过期时间等
        boolean isValid = validateToken(token); // 假设你有一个校验方法

        if (!isValid) {
            responseUtils.setCode(ReturnCodeEnum.ILLEGAL_TOKEN_ERR.getCode());
            responseUtils.setMsg(ReturnCodeEnum.ILLEGAL_TOKEN_ERR.getMsg());
            response.getWriter().write(String.valueOf(new JSONObject(responseUtils)));
            return null;
        }
        return token;
    }

    /**
     * 模拟 Token 校验逻辑，你可以替换成 JWT 校验等
     * 原逻辑：解密 token 得到 userid，用 userid 拼接 key 查 redis，校验 token 是否匹配，并刷新过期时间
     */
    private boolean validateToken(String oldToken) {
        // 解密token后，拼接userid作为key去查redis，最后还要重新赋值过期时间
        String userId = null;
        try {
            // 解密出现异常，证明token不对
            userId = TokenUtils.decrypt(oldToken);
        } catch (Exception e) {
            return false;
        }
        String key = appCode + "_login_" + userId;
        String realToken = null;

        // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
        // ✅ Redis 降级改造开始：先判断 Redis 是否可用
        if (redisService.isRedisAvailable()) {
            // ✅ Redis 正常时的原逻辑：查 Redis，校验 token，刷新过期时间
            realToken = redisService.getObject(key, String.class);
            if (realToken == null || (!oldToken.equals(realToken))) {
                return false;
            } else {
                // 校验通过，重新赋值 token 固定+随机过期时间
                redisService.resetObjectWithExpire(key, realToken, baseExpireTime + RandomUtil.randomLong(0, 1000), TimeUnit.SECONDS);
            }
        } else {
            // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
            // ✅ Redis 不可用时的降级处理：查询数据库中的 token 进行校验

            log.warn("[降级] Redis 不可用，尝试从数据库 user_login_token 表中查询 token 进行校验，userId={}", userId);

            // 调用 Service 查询数据库中该用户存储的 token
            try {
                Optional<UserLoginToken> tokenOpt = userLoginTokenService.getTokenByUserId(userId);
                if (tokenOpt.isPresent()) {
                    UserLoginToken dbTokenRecord = tokenOpt.get();
                    String dbToken = dbTokenRecord.getToken();
                    LocalDateTime expireTime = dbTokenRecord.getExpireTime();

                    if (oldToken != null && oldToken.equals(dbToken)) {
                        if (expireTime != null && expireTime.isAfter(LocalDateTime.now())) {
                            log.info("[降级校验成功] 数据库中的 token 与传入 token 一致，且未过期，userId={}, token 校验通过", userId);
                            return true;
                        } else {
                            log.warn("[降级校验失败] 数据库中的 token 已过期，userId={}, expireTime={}", userId, expireTime);
                        }
                    } else {
                        log.warn("[降级校验失败] 数据库中的 token 与传入 token 不一致，userId={}, dbToken={}, inputToken={}", userId, dbToken, oldToken);
                    }
                } else {
                    log.warn("[降级校验失败] 数据库中未找到用户 userId={} 的 token 记录", userId);
                }
            } catch (Exception e) {
                log.error("[降级校验异常] 查询数据库 user_login_token 表失败，userId={}, 异常信息: ", userId, e);
            }

            // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
            // 降级查询数据库后仍然失败：token 无效，返回 false
            return false;
            // ✅ 降级改造结束
            // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
        }
        // ✅ 降级改造结束
        // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

        return true;
    }

    /**
     * 保存操作日志（从 ThreadLocal 获取当前用户 token，再查用户信息）
     */
    public void saveLog(String operateInfo) {

        H5UserinfoPo h5UserinfoPo = springUtils.getUserByToken(ThreadLocalContext.getToken());

        SaveLogInfoVo saveLogInfoVo = SaveLogInfoVo.builder()
                .userid(h5UserinfoPo.getUserId())
                .deptno(h5UserinfoPo.getDeptNo().toString())
                .userorg(h5UserinfoPo.getUserOrg())
                .clientip(ThreadLocalContext.getIp())
                .logindate(DateUtil.today())
                .logintime(DateUtil.now())
                .operation(operateInfo)
                .loginway("0").build();

        logInfoDao.insertLogInfo(saveLogInfoVo);
    }

    /**
     * 保存登录日志（直接传入用户信息和 request，用于登录成功后记录）
     */
    public void saveLogWhenLogin(String operateInfo, H5UserinfoPo h5UserinfoPo, HttpServletRequest request) {

        SaveLogInfoVo saveLogInfoVo = SaveLogInfoVo.builder()
                .userid(h5UserinfoPo.getUserId())
                .deptno(h5UserinfoPo.getDeptNo().toString())
                .userorg(h5UserinfoPo.getUserOrg())
                .clientip(IpUtils.getClientIp(request))
                .logindate(DateUtil.today())
                .logintime(DateUtil.now())
                .operation(operateInfo)
                .loginway("0").build();

        logInfoDao.insertLogInfo(saveLogInfoVo);
    }

    /**
     * 读取xml文件，转化为bean
     * @param xmlPath
     * @param clazz
     * @return
     * @param <T>
     */
    public <T>T convertDocument2Obj(String xmlPath,Class<T> clazz){
        try {
            // 1. 创建 JAXBContext，根类是 XmlToObjectAllInOne
            JAXBContext context = JAXBContext.newInstance(clazz);

            // 2. 创建 Unmarshaller
            Unmarshaller unmarshaller = context.createUnmarshaller();

            // 3. 读取 XML 文件（请替换为你的实际文件路径，比如 "example.xml"）
            File xmlFile = new File(xmlPath);
            T obj = (T) unmarshaller.unmarshal(xmlFile);

            return obj;
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }
}